﻿package kliment.display {
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.DisplayObject;
	import flash.display.Loader;
	import flash.display.PixelSnapping;
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.geom.Matrix;
	import flash.geom.Rectangle;
	import flash.net.URLRequest;
	import kliment.events.BitmapLoaderEvent;
	import kliment.math.Calc;
	import kliment.utils._Sprite;
	import kliment.utils.Transformation;


	/**
     * Dispatched when .....
     */
    [Event(name = 'laod_open', type = 'kliment.events.BitmapLoaderEvent')]
	/**
     * Dispatched when .....
     */
    [Event(name = 'laod_complete', type = 'kliment.events.BitmapLoaderEvent')]
	/**
     * Dispatched when .....
     */
    [Event(name = 'laod_pogress', type = 'kliment.events.BitmapLoaderEvent')]
	/**
     * Dispatched when .....
     */
    [Event(name = 'laod_io_error', type = 'kliment.events.BitmapLoaderEvent')]

	/**
	 * ...
	 * @author	Kliment
	 * @version	1.0.6
	 *
	 * class name: kliment.display.BitmapLoader
	 */
	public final class BitmapLoader extends Bitmap {
		static public const MIN_WIDTH:Number = 1;
		static public const MIN_HEIGHT:Number = 1;
		static public const MAX_WIDTH:Number = 2880;
		static public const MAX_HEIGHT:Number = 2880;

		private var _width:Number;
		private var _height:Number;
		private var _caching:Boolean;
		private var _crop:Boolean;
		private var _centred:Boolean;
		private var _cut:Boolean;
		private var _bodyBMP:BitmapData;
		private var _resultBMP:BitmapData;
		private var _loader:Loader;

		private var _bytesLoaded:Number = 0;
		private var _bytesTotal:Number = 0;
		private var _ioErrorEventText:String = '';

		public var increase:Boolean = true;
		public var minWidth:Number = 1;
		public var minHeight:Number = 1;

		public function BitmapLoader(width:int, height:int, pixelSnapping:String = "never", smoothing:Boolean = false) {
			super(null, pixelSnapping, smoothing);
			_width = Calc.inInterval(MIN_WIDTH, MAX_WIDTH, width) || MIN_WIDTH;
			_height = Calc.inInterval(MIN_HEIGHT, MAX_HEIGHT, height) || MIN_HEIGHT;
		}

		public function load(url:String, caching:Boolean = false, crop:Boolean = true, centred:Boolean = true, cut:Boolean = false):void {
			_caching = caching;
			_crop = crop;
			_centred = centred;
			_cut = cut;
			if (_loader) {
				_loader.unload();
				_loader = null;
			}
			_loader = new Loader();
			_loader.contentLoaderInfo.addEventListener(Event.OPEN, _loadHandler);
			_loader.contentLoaderInfo.addEventListener(Event.COMPLETE, _loadHandler);
			_loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, _loadHandler);
			_loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, _loadHandler);
			_loader.load(new URLRequest(url));
		}

		private function _loadHandler(event:Event):void {
			var typeNewEvent:String;
			switch(event.type) {
				case Event.OPEN:
					typeNewEvent = BitmapLoaderEvent.OPEN;
				break;
				case Event.COMPLETE:
					typeNewEvent = BitmapLoaderEvent.COMPLETE;
					clear();

					_bodyBMP = new BitmapData(_loader.width, _loader.height, true, 0x000000);
					_bodyBMP.draw(_loader.content);

					var newWidth:Number;
					var newHeight:Number;
					if (increase) {
						newWidth = Math.max(_width, minWidth);
						newHeight = Math.max(_height, minHeight);
					} else {
						newWidth = Math.min(_width, _bodyBMP.width);
						newHeight = Math.min(_height, _bodyBMP.height);
					}

					_resultBMP = resizeBitmapData(_bodyBMP, newWidth, newHeight, _crop, _centred, _cut);
					bitmapData = _resultBMP;

					if (!_caching) {
						_bodyBMP.dispose();
						_bodyBMP = null;
					}

					_loader.contentLoaderInfo.removeEventListener(Event.OPEN, _loadHandler);
					_loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, _loadHandler);
					_loader.contentLoaderInfo.removeEventListener(ProgressEvent.PROGRESS, _loadHandler);
					_loader.contentLoaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, _loadHandler);
				break;
				case ProgressEvent.PROGRESS:
					typeNewEvent = BitmapLoaderEvent.POGRESS;
					var progressEvent:ProgressEvent = event as ProgressEvent;
					_bytesLoaded = progressEvent.bytesLoaded;
					_bytesTotal = progressEvent.bytesTotal;
				break;
				case IOErrorEvent.IO_ERROR:
					typeNewEvent = BitmapLoaderEvent.IO_ERROR;
					var ioErrorEvent:IOErrorEvent = event as IOErrorEvent;
					_ioErrorEventText = ioErrorEvent.text;
				break;
			}
			if (typeNewEvent)
				dispatchEvent(new BitmapLoaderEvent(typeNewEvent, false, false, _bytesLoaded, _bytesTotal, _ioErrorEventText))
		}

		public static function resizeBitmapData(bitmapData:BitmapData, newWidth:Number, newHeight:Number, crop:Boolean = true, centred:Boolean = false, cut:Boolean = false):BitmapData {
			var matrix:Matrix = _getResizeMatrix(bitmapData, newWidth, newHeight, crop, centred);
			if (cut && !centred) {
				newWidth = Math.min(newWidth, matrix.a * bitmapData.width);
				newHeight = Math.min(newHeight, matrix.d * bitmapData.height);
			}
			var newBitmapData:BitmapData = new BitmapData(newWidth, newHeight, true, 0x000000);
			newBitmapData.draw(bitmapData, matrix, null, null, null, true);
			return newBitmapData;
		}

		static private function _getResizeMatrix(target:BitmapData, width:Number, height:Number, crop:Boolean, centred:Boolean):Matrix {
			var rectangle:Rectangle = Transformation.putInSpace(target, width, height, crop, centred);
			return new Matrix(rectangle.width / target.width, 0, 0, rectangle.height / target.height, rectangle.x, rectangle.y);
		}

		public function get originalBitmap():BitmapData {
			return _bodyBMP;
		}

		public function get finalBitmap():BitmapData {
			return _resultBMP;
		}

		public function cloneBitmap():Bitmap {
			return new Bitmap(_resultBMP.clone());
		}

		public function clear():void {
			if (bitmapData)
				bitmapData.dispose();
			if (_bodyBMP)
				_resultBMP.dispose();
			if (_bodyBMP)
				_bodyBMP.dispose();
			bitmapData = null;
			_resultBMP = null;
			_bodyBMP = null;
		}

		public function remove():DisplayObject {
			clear();
			return _Sprite.remove(this);
		}
	}

}